package game;
import values.Vector;
import be.kuleuven.cs.som.annotate.*;
import exception.*;
import game.Util;

/**
 * A class of space objects involving an x and y position, an x and y velocity, a maximum velocity and a radius.
 * 
 * @invar	The radius of each spaceObject is a valid radius.
 * 			| isValidRadius(getRadius())
 * @invar	The x and y position of each spaceObject must be a valid position.
 * 			| isValidPosition(getxPosition(),getyPosition())
 * @invar	The x and y velocity of each spaceObject must be a valid velocity.
 * 			| isValidVelocity(getVelocity())
 * @invar	The maxVelocity of each spaceObject must be a valid maximum velocity.
 * 			| isValidMaxVelocity(getMaxVelocity())
 * @invar	| isValidWorld(getWorld())
 * @invar	| isValidMass(getMass())
 * 
 * @author 	JeroenGillis && ThomasProvoost
 * @version	1.1
 *
 */
public class SpaceObject {

	private Vector position = new Vector(0,0);
	private Vector velocity = new Vector(0,0);
	private final double maxVelocity;
	private final double radius;
	private World world;
	private boolean isTerminated = false;
	private final double mass;
	
	/**
	 * Initialize this new spaceObject with position, velocity, direction, maximum velocity,radius, world and mass. 
	 * @param	xPosition
	 * 			The x coordinate for this new spaceObject.
	 * @param	yPosition
	 * 			The y coordinate for this new spaceObject.
	 * @param	xVelocity
	 * 			The x velocity for this new spaceObject.
	 * @param	yVelocity
	 * 			The y velocity for this new spaceObject.
	 * @param	radius
	 * 			The final radius for this new spaceObject.
	 * @param	maxVelocity
	 * 			The final maxVelocity for this new spaceObject.
	 * @param	world
	 * 			The world for this new spaceObject.
	 * @param	mass
	 * 			The mass for this new spaceObject.
	 * @post	The radius of this new spaceObject is equal to the given radius.
	 * 			| (new this).getRadius() == radius
	 * @post	The maximum velocity of this new spaceObject is set to the given maxVelocity.
	 * 			| (new this).getMaxVelocity() == maxVelocity
	 * @post	The mass of this new spaceObject is set to the given mass.
	 * 			| (new this).getMass() == mass
	 * @effect	The position of this new spaceObject is a vector with components xPosition and yPosition.
	 * 			| setPosition(xPosition,yPosition)
	 * @effect	The velocity of this new spaceObject is a vector with components xVelocity and yVelocity.
	 * 			| setVelocity(xVelocity,yVelocity)
	 * @effect	The world of this new spaceObject is the given world.
	 * 			| placeInWorld(world)
	 * @throws	IllegalArgumentException
	 * 			If the given radius is invalid or if at least one of the given position coordinates is invalid.
	 * 			| !isValidRadius(radius) || !isValidPosition(xPosition,yPosition) || ! isValidMass(mass)
	 */
	@Raw
	public SpaceObject(double xPosition, double yPosition, double xVelocity, double yVelocity, double radius, double maxVelocity, World world, double mass) 
				throws IllegalArgumentException	{
		if(!isValidRadius(radius)) {
			throw new IllegalArgumentException();
		}
		this.radius = radius;
		if(!isValidMaxVelocity(maxVelocity)) {
			maxVelocity = 300000.0;
		}
		this.maxVelocity = maxVelocity;
		if(! isValidMass(mass))
			throw new IllegalArgumentException();
		this.mass = mass;
		setPosition(xPosition,yPosition);
		setVelocity(xVelocity,yVelocity);
		placeInWorld(world);
	}

	/**
	 * Create a new spaceObject with given position and velocity vectors, radius, maxVelocity, world and mass. 
	 * @effect	| this(position.getxComp(),position.getyComp(),velocity.getxComp(),velocity.getyComp(),radius,maxVelocity, world, mass)
	 */
	@Raw
	public SpaceObject(Vector position, Vector velocity, double radius,double maxVelocity, World world, double mass) {
		this(position.getxComp(),position.getyComp(),velocity.getxComp(),velocity.getyComp(),radius,maxVelocity, world, mass);
	}
	
	/**
	 * Initialize this new space object in it's default state. 
	 * @effect	| this(1.0,1.0,0.0,0.0,1.0, 300000, null, 1000)
	 */
	@Raw
	public SpaceObject() {
		this(1.0,1.0,0.0,0.0,1.0, 300000, null, 1000);
	}

	/**
	 * Return the position vector of this spaceObject.
	 */
	@Basic @Raw
	public Vector getPosition() {
		return position;
	}

	/**
	 * Return the position of this spaceObject on the x axis. 
	 */
	@Raw
	public double getxPosition() {
		return getPosition().getxComp();
	}

	/**
	 * Return the position of this spaceObject on the y axis. 
	 */
	@Raw
	public double getyPosition() {
		return getPosition().getyComp();
	}

	/**
	 * Return a boolean reflecting whether this spaceObject can have the given position as a x position.
	 * @param	xPosition
	 * 			The xPosition to be checked.
	 * @param	yPosition
	 * 			The yPosition to be checked.
	 * @return	| if(Double.isNaN(xPosition) || Double.isNaN(yPosition)) result ==  false
	 *			| result == ( !isInWorld() || isInWorldBoundaries(xPosition, yPosition) )
	 */
	public boolean isValidPosition(double xPosition, double yPosition) {
		if(Double.isNaN(xPosition) || Double.isNaN(yPosition)) return false;
		if(!isInWorld()) return true;
		return isInWorldBoundaries(xPosition, yPosition,this.getWorld());
	}
	
	/**
	 * Return a boolean reflecting whether this space object is completely in the given world 
	 * 	if it were centered at the given xPosition and yPosition.
	 * @return	| if(world == null) result == true
	 * 			| if(! Util.fuzzyIsInInterval(xPosition, getRadius(), world.getWidth() - getRadius()))
				|	return false;
				| if(! Util.fuzzyIsInInterval(yPosition, getRadius(), world.getHeight() - getRadius()))
				|	return false;
	 *			| else result ==  true
	 */
	public boolean isInWorldBoundaries(double xPosition, double yPosition, World world) {
		if(world == null) return true;
		
		if(! Util.fuzzyIsInInterval(xPosition, getRadius(), world.getWidth() - getRadius()))
			return false;
		if(! Util.fuzzyIsInInterval(yPosition, getRadius(), world.getHeight() - getRadius()))
			return false;
		
		return true;
	}
	
	/**
	 * Return a boolean reflecting whether this space object lies entirely in its world. 
	 * @return isInWorldBoundaries(getxPosition(),getyPosition())
	 */
	public boolean isInWorldBoundaries() {
		return isInWorldBoundaries(getxPosition(),getyPosition(),getWorld());
	}
	

	/**
	 * Set the position of this spaceObject to the given vector position
	 * @param 	position
	 * 			The new position of this spaceObject.
	 * @post	The position of this spaceObject is set to the given vector position
	 * 			| (new this).getPosition() == position
	 * @throws	IllegalStateException
	 * 			| isTerminated()
	 */
	@Raw
	public void setPosition(Vector position) throws IllegalStateException {
		if(isTerminated())
			throw new IllegalStateException();
		this.position = position;
	}

	/**
	 * Set the position of this spaceObject to the given xPosition and yPosition.
	 * @param 	xPosition
	 * 			The new x position of this spaceObject.
	 * @param 	yPosition
	 * 			The new y position of this spaceObject.
	 * @post	The x position of this spaceObject is set to the given value xPosition.
	 * 			| (new this).getxPosition() == xPosition
	 * @post	The y position of this spaceObject is set to the given value yPosition.
	 * 			| (new this).getyPosition() == yPosition 
	 * @throws	IllegalStateException
	 * 			| isTerminated()
	 * @throws	IllegalArgumentException
	 * 			If the given position is not a valid position for this spaceObject.
	 * 			| ( !isValidPosition(xPosition,yPosition) )
	 */
	@Raw
	public void setPosition(double xPosition, double yPosition)
			throws IllegalArgumentException, IllegalStateException {
				if(isTerminated())
					throw new IllegalStateException();
				if (!isValidPosition(xPosition,yPosition)) {
					throw new IllegalArgumentException();
				}
				else {
					setPosition(new Vector(xPosition,yPosition));
				}
			}

	/**
	 * Move this spaceObject during a given time.
	 * @param	duration
	 * 			The duration this spaceObject is moving.
	 * @post	If the given duration is valid, the x position of this spaceObject is augmented by the given duration multiplied by the x velocity of this spaceObject
	 * 			and the y position of this spaceObject is augmented by the given duration multiplied by the y velocity of this spaceObject.
	 * 			|	if (isValidDurationToMove(duration)) 
	 * 			|		(new this).getxPosition() == this.getxPosition() + duration*getxVelocity()
				|		(new this).getyPosition() == this.getyPosition() + duration*getyVelocity()
	 * @throws	IllegalDurationException(duration)
	 * 			If the given duration is not valid.
	 * 			| ! isValidDurationToMove(duration) 
	 * @throws	IllegalStateException
	 * 			| isTerminated()
	 */
	public void move(double duration) throws IllegalDurationException, IllegalStateException {
			if(isTerminated())
				throw new IllegalStateException();
			if(! isValidDurationToMove(duration)) {
				throw new IllegalDurationException(duration);
			}
			else {
				Vector newPosition = new Vector(getxPosition() + duration*getxVelocity(),getyPosition() + duration*getyVelocity());
				setPosition(newPosition);
			return;
			}
		}

	/**
	 * Return a boolean reflecting whether the given dt is a valid duration to move this spaceObject.
	 * @param 	dt
	 * 			The duration to be checked.
	 * @return	False if the given duration is not a number or a negative number. Else the result is true.
	 * 			| if(dt < 0 || dt == Double.NaN) result == false
	 * 			| else result == true
	 */
	public boolean isValidDurationToMove(double dt) {
		if(dt < 0 || Double.isNaN(dt)) return false;
		else return true;
	}

	/**
	 * Return the velocity vector of this spaceObject
	 */
	@Basic	@Raw
	public Vector getVelocity() {
		return this.velocity;
	}

	/**
	 * Return the velocity of this spaceObject in the direction of the x axis.
	 * @return	| result == getVelocity().getxComp()
	 */
	@Raw
	public double getxVelocity() {
		return getVelocity().getxComp();
	}

	/**
	 * Return the velocity of this spaceObject in the direction of the y axis.
	 * @return	| result == getVelocity().getyComp()
	 */
	@Raw
	public double getyVelocity() {
		return getVelocity().getyComp();
	}

	/**
	 * Return the total velocity of this spaceObject.
	 * @return	Return the length of the velocity vector. 
	 * 			| result == Math.sqrt(Math.pow(getxVelocity(),2)+Math.pow(getyVelocity(),2))
	 */
	public double getVelocityLength() {
		return getVelocity().getLength();
	}

	/**
	 * Set the velocity of this spaceObject to the given vector velocity
	 * @param	velocity
	 * 			The new velocity of this spaceObject.
	 * @post	| if(velocity == null) (new this) == this
	 * @post	| (new this).getVelocity() == velocity
	 * @effect	| if(!isValidVelocity(velocity)) 
	 *			|	setVelocity(new Vector(getMaxVelocity(),velocity.getAngle(),true))
	 *@throws	IllegalStateException
	 *			| isTerminated()
	 */
	@Raw
	public void setVelocity(Vector velocity) throws IllegalStateException{
		if(isTerminated())
			throw new IllegalStateException();
		if(velocity == null)
			return;
		if(!isValidVelocity(velocity)) 
			setVelocity(new Vector(getMaxVelocity(),velocity.getAngle(),true));
		else 
			this.velocity = velocity;
	}

	/**
	 * Set the velocity of this spaceObject to a new vector with components the given xVelocity and yVelocity.
	 * @param 	xVelocity
	 * 			The x component of the new velocity of this spaceObject.
	 * @param	yVelocity
	 * 			The y component of the new velocity of this spaceObject.
	 * @effect	The new velocity of this spaceObject is set to a vector with components xVelocity and yVelocity
	 * 			| setVelocity(new Vector(xVelocity,yVelocity))
	 */
	@Raw
	public void setVelocity(double xVelocity, double yVelocity) throws IllegalStateException {
		setVelocity(new Vector(xVelocity,yVelocity));
	}

	/**
	 * Method returns true if this spaceObject has an acceptable velocity.
	 * @return	If the given velocity is null, the result is false. 
	 * 			| if(velocity == null) result == false
	 * @return	If the velocity length exceeds the maximum velocity the result is false.
	 * 			| result == velocity.isSmallerThan(getMaxVelocity())
	 */
	public boolean isValidVelocity(Vector velocity) {
		if(velocity == null) return false;
		else if(! velocity.isSmallerThan(getMaxVelocity())) return false;
		else return true;
	}

	/**
	 * Return the maximum velocity of this spaceObject.
	 */
	@Basic
	@Immutable
	@Raw
	public double getMaxVelocity() {
		return maxVelocity;
	}

	/**
	 * Return a boolean reflecting whether the given maxVelocity is a valid maxVelocity.
	 * @param	maxVelocity
	 * 			The given maximum velocity to be checked.
	 * @return	True if the given maxVelocity is positive and equal to or less than 300000. 
	 * 			| if ( 0 <= maxVelocity <= 300000) result == true
	 */
	public boolean isValidMaxVelocity(double maxVelocity) {
		return (0 <= maxVelocity) && (maxVelocity <= 300000);
	}

	/**
	 * Return the final radius of this spaceObject.
	 */
	@Basic
	@Immutable
	@Raw
	public double getRadius() {
		return radius;
	}

	/**
	 * Return a boolean reflecting whether the given radius is a valid radius for this spaceObject.
	 * @return	True if the given radius is finite and larger than 0. 
	 * 			| if( radius >0 && radius != Double.POSITIVE_INFINITY ) result == true
	 * 			| else result == false
	 */
	public boolean isValidRadius(double radius) {
		return ( radius > 0 && radius != Double.POSITIVE_INFINITY );
	}

	/**
	 * Return a double with the distance between this spaceObject and the given other spaceObject.
	 * @param otherObject
	 * 			The other spaceObject.
	 * @return	The distance between this spaceObject and the other spaceObject. The distance is negative if the spaceObjects overlap.
	 * 			| result == (this.getPosition() - otherObject.getPosition()).getLength() - this.getRadius() - otherObject.getRadius() 			
	 * @return	The distance between this spaceObject and itself is 0.
	 * 			| if (otherObject == this) result == 0
	 * @throws	NullPointerException
	 * 			| otherObject == null
	 * @throws	IllegalDistanceException
	 * 			If at least one of the spaceObjects has an infinite position coordinate
	 * 			| ( this.getPosition().isInfinite() || otherObject.getPosition().isInfinite() ) 
	 */
	public double getDistanceBetween(SpaceObject otherObject) throws NullPointerException,
			IllegalDistanceException, IllegalStateException {
				if(otherObject == null)
					throw new NullPointerException();	
				if(isTerminated() || otherObject.isTerminated())
					throw new IllegalStateException();
				if(this == otherObject) return 0.0;
				else if( this.getPosition().isInfinite() || otherObject.getPosition().isInfinite() ) {
					throw new IllegalDistanceException();
				}
				else { return (this.getPosition().subtract(otherObject.getPosition())).getLength() - this.getRadius() - otherObject.getRadius();
				}
			}

	/**
	 * A boolean reflecting whether this spaceObject and the given other spaceObject overlap.
	 * @param otherObject
	 * 			The other spaceObject to be compared with this spaceObject.
	 * @return 	If and only if the distance between this spaceObject and the other spaceObject is not positive the result is true.
	 * 			| if(this.getDistanceBetween(otherObject) <= 0 ) result == true
	 * @throws	NullPointerException
	 * 			| otherObject == null
	 * @throws	IllegalDistanceException
	 * 			If the distance between this spaceObject and the other spaceObject is undefined.
	 * 			| if(this.getDistanceBetween(otherObject) throws IllegalDistanceException)
	 */
	public boolean overlap(SpaceObject otherObject) throws NullPointerException,
			IllegalDistanceException {
				if (Util.fuzzyEquals(this.getDistanceBetween(otherObject), 0) || this.getDistanceBetween(otherObject) < 0) return true;
				else return false;
			}
	
	private boolean touch(SpaceObject otherObject) {
		// TODO Auto-generated method stub
		return Util.fuzzyEquals(this.getDistanceBetween(otherObject), 0);
	}

	/**
	 * Return a double predicting the time to collision between this spaceObject and the given other spaceObject. 
	 * @param otherObject
	 * 			The other spaceObject
	 * @return	The time to collision between this spaceObject and the given spaceObject.
	 * 			| x == getTimeToCollision(otherObject) <=>
	 * 			| this.move(x),otherObject.move(x) => this.overlap(otherObject) == true
	 * 			| && if(0 < x' < x) this.move(x'),otherObject.move(x') => this.overlap(otherObject) == false
	 * @return	Positive infinity if the spaceObjects never collide.
	 * 			| if(0 < x) this.move(x),otherObject.move(x) => this.overlap(otherObject) == false
	 * @throws	NullPointerException
	 * 			otherObject does not exist.
	 * 			| otherObject == null
	 */
	public double getTimeToCollision(SpaceObject otherObject) throws NullPointerException,
			IllegalDistanceException, IllegalStateException{
				if(isTerminated() || otherObject.isTerminated())
					throw new IllegalStateException();
				if( this.getPosition().isInfinite() || otherObject.getPosition().isInfinite() ) {
					throw new IllegalDistanceException();
				}
				if(otherObject == this) return Double.POSITIVE_INFINITY;
				
				Vector deltaR = this.getPosition().subtract(otherObject.getPosition());
				Vector deltaV = this.getVelocity().subtract(otherObject.getVelocity());
				
				double dVdR = deltaR.scalarProduct(deltaV);
				if(dVdR >= 0.0) return Double.POSITIVE_INFINITY;
				
				double sigmaSquared = Math.pow(this.getRadius() + otherObject.getRadius(),2);
				double deltaVSquared = deltaV.scalarProduct(deltaV);
				double d = Math.pow(dVdR, 2) - deltaVSquared*(deltaR.scalarProduct(deltaR)-sigmaSquared);
				
				if(d <= 0) return Double.POSITIVE_INFINITY;
				
				return Math.abs(-(dVdR+Math.sqrt(d))/(deltaVSquared));
			}

	/**
	 * Return the position of this spaceObject when it collides with the given other spaceObject. Return null if they never collide.
	 * @param	otherObject
	 * 			The spaceObject to be compared with this spaceObject.
	 * @return	An array of two doubles with on index 0 the x coordinate and on index 1 the y coordinate of the position of this spaceObject when it collides with the other spaceObject.
	 * 			| if(0 <= getTimeToCollision(otherObject) < Double.POSITIVE_INFINITY) 
	 * 			|	result == ( double[0] == this.move(getTimeToCollision(otherObject)).getxPosition() 
	 * 			|				&& double[1] == this.move(getTimeToCollision(otherObject)).getyPosition() )
	 * @return	null when the spaceObjects never collide.
	 * 			| if(getTimeToCollision(otherObject) == Double.POSITIVE_INFINITY) result == null
	 * @throws	CollisionException
	 * 			This spaceObject and the other spaceObject overlap.
	 * 			| this.overlap(otherObject) 
	 */
	public double[] getCollisionPosition(SpaceObject otherObject) throws NullPointerException, IllegalDistanceException, IllegalStateException {				
				if (this.overlap(otherObject) && ! touch(otherObject)) {
					throw new CollisionException();
				}
				double timeToCollision = getTimeToCollision(otherObject);
				if(timeToCollision == Double.POSITIVE_INFINITY) {
					return null;
				}
				Vector position1 = this.getPosition();
				Vector position2 = otherObject.getPosition();
				Vector velocity1 = this.getVelocity();
				Vector velocity2 = otherObject.getVelocity();
				Vector collisionPosition1 = position1.add(velocity1.multiplyScalar(timeToCollision));
				Vector collisionPosition2 = position2.add(velocity2.multiplyScalar(timeToCollision));
				Vector difference = collisionPosition2.subtract(collisionPosition1);
				double radiusRatio = this.getRadius()/(this.getRadius()+otherObject.getRadius());
				double[] collisionPosition = new double[2];
				collisionPosition[0] = collisionPosition1.add(difference.multiplyScalar(radiusRatio)).getxComp();
				collisionPosition[1] = collisionPosition1.add(difference.multiplyScalar(radiusRatio)).getyComp();
				return collisionPosition;				
			}

	/**
	 * Return the world of this spaceObject.
	 */
	@Basic
	public World getWorld() {
		return world;
	}
	
	/**
	 * Return a boolean reflecting whether this space object is in a world.
	 */
	public boolean isInWorld() { 
		return getWorld() != null;
	}

	/**
	 * Return a boolean reflecting the given world is a valid world for this spaceObject
	 */
	public boolean isValidWorld(World world) {
		if(isTerminated()) {
			return world == null;
		}
		else return true;
	}
	/**
	 * Set the world of this spaceObject to the given world.
	 * @param 	world
	 * 			The new world of this spaceObject.
	 * @post	| (new this).getWorld() == world
	 * @post	| (new world).containsSpaceObject(this) == true
	 * @throws	IllegalStateException
	 * 			| isTerminated()
	 */
	public void placeInWorld(World world) throws IllegalStateException {//kan IllegalArgument throwen via removeSpObj
		if(world == null) setWorld(world);
		else {
			world.addSpaceObject(this);			
		}
	}
	
	public void setWorld(World world) {
		if(! canHaveAsWorld(world))
			throw new IllegalStateException();
		if(getWorld() != null && getWorld().containsSpaceObject(this))
			getWorld().removeSpaceObject(this);
		this.world = world;
	}
	
	/**
	 * Return a boolean reflecting whether this space object can have the given world as a world.
	 * @param world
	 * 			The world to be checked.
	 * @return	| if(this.isTerminated() || !isInWorldBoundaries(getxPosition(), getyPosition(), world) )
	 *			|		result == (world == null)
	 *			| else 	result == true
	 */
	public boolean canHaveAsWorld(World world) {
		if(this.isTerminated() )
			return world == null;
		if(!isInWorldBoundaries(getxPosition(), getyPosition(), world))
			return false;
		return true;
	}

	/**
	 * Return a boolean reflecting whether this spaceObject is terminated.
	 */
	@Basic
	public boolean isTerminated() {
		return isTerminated;
	}

	/**
	 * Terminate this spaceObject.
	 */
	//Heb ik public gemaakt om te testen.
	public void setTerminated() {
		if(isTerminated())
			throw new IllegalStateException();
		this.isTerminated = true;
	}
	
	/**
	 * Terminate this spaceObject. 
	 * @post	| (new this.getWorld()).containsSpaceObject(this) == false
	 * @post	| (new this).getWorld() == null
	 * @effect	| setTerminated()
	 * @throws	IllegalStateException
	 * 			| isTerminated()
	 */
	public void terminate() throws IllegalStateException { //zit misschien in fout, geeft in 2 verschillende tests een IllegalArgumentException
		if(isTerminated())
			throw new IllegalStateException();
		World world = this.getWorld();
		if(world != null) {
			world.removeSpaceObject(this);
			this.placeInWorld(null);
		}
		setTerminated();
	}
	
	/**
	 * Hit a boundary of the world of this space object. 
	 * @post	| if(willHitVerticalBoundary()) 
	 * 			|	(new this).getxVelocity() == (this.getxVelocity())*(-1)
	 * @post	| if(willHitVerticalBoundary()) 
	 * 			|	(new this).getyVelocity() == (this.getyVelocity()).multiplyScalar(-1)
	 * @throws	IllegalStateException
	 * 			| (getWorld() == null) || (this.isTerminated()) || ((! willHitVerticalBoundary()) && (! willHitHorizontalBoundary()))
	 */
	public void hitBoundary() throws IllegalStateException {
		if(getWorld() == null || this.isTerminated())
			throw new IllegalStateException();
		if(hasHitVerticalBoundary())
			setVelocity(getxVelocity()*(-1),getyVelocity());
		if(hasHitHorizontalBoundary())
			setVelocity(getxVelocity(),getyVelocity()*(-1));
		else if ( (!hasHitVerticalBoundary()) && (!hasHitHorizontalBoundary()))
			throw new IllegalStateException();
	}
	
	/**
	 * Return a boolean reflecting whether this space object has hit a vertical boundary of the world of this ship.
	 * @return	result == ( ( getxPosition() >= (getWorld().getWidth() - getRadius()) ) || (getxPosition() <= getRadius() ) )
	 */
	public boolean hasHitVerticalBoundary() {
		if(Util.fuzzyEquals(getxPosition(), getRadius()) || Util.fuzzyEquals(getxPosition(), getWorld().getWidth() - getRadius()))
			return true;
		else
			return (getxPosition() >= (getWorld().getWidth() - getRadius())) || (getxPosition() <= getRadius());
	}
	
	/**
	 * Return a boolean reflecting whether this space object has hit a horizontal boundary of the world of this ship.
	 * @return	result == ( ( getyPosition() >= (getWorld().getHeight() - getRadius()) ) || (getyPosition() <= getRadius() ) )
	 */
	public boolean hasHitHorizontalBoundary() {
		if(Util.fuzzyEquals(getyPosition(), getRadius()) || Util.fuzzyEquals(getyPosition(), getWorld().getHeight() - getRadius()))
			return true;
		else
			return (getyPosition() >= (getWorld().getHeight() - getRadius())) || (getyPosition() <= getRadius());
	}
	
	/**
	 * If the given space object overlaps with this space object, they will collide.
	 * @throws	IllegalArgumentException
	 * 			| otherObject == null || otherObject.isTerminated() || this.getWorld() != otherObject.getWorld() || !overlap(otherObject)
	 * @throws	IllegalStateException
	 * 			| this.isTerminated() || getWorld() == null
	 */
	public void collideWith(SpaceObject otherObject) throws IllegalStateException,IllegalArgumentException {
		
		if(this.isTerminated() || getWorld() == null) 
			throw new IllegalStateException();
		if(otherObject == null || otherObject.isTerminated() || this.getWorld() != otherObject.getWorld())
			throw new IllegalArgumentException();
		if(!overlap(otherObject)) 
			throw new IllegalArgumentException();
	}
	
	/**
	 * Return the time it takes this space object to hit the boundary of the world it is in.
	 * @return	| x == getTimeToBoundary() <=>
	 * 			| this.move(x) => this.hasHitHorizontalBoundary() == true || this.hasHitVerticalBoundary() == true 
	 * 			| && if(0 < x' < x) this.move(x') => this.hasHitHorizontalBoundary() == false && this.hasHitVerticalBoundary() == false 
	 */
	public double getTimeToBoundary() throws IllegalStateException {
		if(!isInWorld() || isTerminated()) 
			throw new IllegalStateException();
		double timeToVertical;
		if(getxVelocity() > 0) timeToVertical = Math.abs((getWorld().getWidth() - getxPosition() - getRadius())/getxVelocity());
		else timeToVertical = Math.abs((getxPosition() - getRadius())/getxVelocity());
		
		double timeToHorizontal;
		if(getyVelocity() > 0) timeToHorizontal = Math.abs((getWorld().getHeight() - getyPosition() - getRadius())/getyVelocity());
		else timeToHorizontal = Math.abs((getyPosition() - getRadius())/getyVelocity());
		
		return Math.min(timeToVertical, timeToHorizontal);
	}
	
	
	public double getMass() {
		return this.mass;
	}
	
	/**
	 * Return a boolean reflecting whether the given mass is a valid mass for this spaceobject.
	 * @param	mass
	 * 			The given mass to be checked.
	 * @return	| result == mass > 0 && ! Double.isNaN(mass)
	 */
	public boolean isValidMass(double mass) {
		return mass > 0 && ! Double.isNaN(mass);
	}
	
	
	

}
